package main

import (
	"errors"
	"fmt"
	"log"
	"myinterface"
	"os"
	"path/filepath"
	"plugin"
	"strings"
	"time"

	"github.com/kataras/iris"
	"github.com/kataras/iris/sessions"
	"github.com/kataras/iris/sessions/sessiondb/badger"
	pg "gopkg.in/pg.v5"
)

type Site struct {
	Conf  *config
	Db    *MyDB
	Sesns *sessions.Sessions
	App   *iris.Application
}

func (site1 *Site) Init(cfgPathName string) error {
	//读入配置文件
	site1.Conf = readToml(cfgPathName)
	if site1.Conf == nil {
		return errors.New("Site Init: readToml error")
	}
	//连接数据库
	site1.Db = NewMyDB(site1.Conf.DbAddr, site1.Conf.DbUser, site1.Conf.DbPwd, site1.Conf.DbName)
	if site1.Db == nil {
		return errors.New("Site Init: database connect error")
	}
	//初始化数据库 users、history ，带条件：if not exists
	sqlInit := []string{`create table if not exists users(id SERIAL,name text unique PRIMARY KEY,md5pwd CHAR(32) not null,level int default 4,other text);`,
		`create table if not exists history(id SERIAL,name text not null,room text not null,msg text not null,time timestamp default NOW(),PRIMARY KEY(id,room));`,
		`comment on table users is '注册用户数据';`,
		`comment on table history is '聊天历史记录';`}
	site1.Db.ExecSqls(sqlInit)
	//初始化 iris.Application
	site1.App = iris.New()
	//设置 views
	site1.App.RegisterView(iris.HTML(site1.Conf.ViewDir, ".html"))
	//设置 static
	site1.App.StaticWeb("/static", site1.Conf.StaticDir)
	//初始化 sessions
	site1.Sesns = sessions.New(sessions.Config{Cookie: "sessionid"})
	db, err := badger.New(site1.Conf.SessionDir)
	if err != nil {
		return err
	}
	site1.Sesns.UseDatabase(db)

	return nil
}

func (site1 *Site) Close() {
	site1.Db.Close()
	site1.Sesns.DestroyAll()
}

//Global 运行于每个handler之前，验证登录状态
func (site1 *Site) Global(ctx iris.Context) {
	defer ctx.Next()
	opt := iris.CookieHTTPOnly(false)
	s1 := site1.Sesns.Start(ctx)
	//log.Printf("SessionID:%s\n", ctx.GetCookie("sessionid"))
	v := s1.GetString("logined")
	if v == "" || v == "no" {
		if ctx.GetCookie("start") == "" {
			t := fmt.Sprintf("%d", time.Now().Unix())
			log.Printf("start:%s\n", t)
			s1.Set("start", t)
			s1.Set("logined", "no")
			ctx.SetCookieKV("addr", ctx.Request().RemoteAddr, opt)
			ctx.SetCookieKV("start", t, opt)
			ctx.SetCookieKV("logined", "no")
		}
	} else if v == "yes" {
		//check is logined
		ctx.SetCookieKV("logined", v)
		ctx.SetCookieKV("chatname", s1.GetString("chatname"))
	}
	log.Printf("Global: %s %s %s\n", ctx.Method(), ctx.Host(), ctx.Path())
}

//设置各种 handler，然后运行服务
func (site1 *Site) Run() {
	site1.App.UseGlobal(site1.Global)
	site1.setUsersHandlers()
	site1.App.Any("/", func(ctx iris.Context) {
		ctx.StatusCode(iris.StatusTemporaryRedirect)
		ctx.Redirect("/user/")
	})

	site1.App.Run(iris.Addr(site1.Conf.WWWAddr))
}

func (site1 *Site) LoadPlug(name string, m1 map[string]interface{}) error {
	p, err := plugin.Open(name)
	if err != nil {
		return err
	}
	f, err := p.Lookup("New")
	if err != nil {
		return err
	}
	fn, ok := f.(func(*iris.Application, *sessions.Sessions, *pg.DB, map[string]interface{}) interface{})
	if ok == false {
		return errors.New(fmt.Sprintf("plugin.New type error: %s", name))
	}
	h := fn(site1.App, site1.Sesns, site1.Db.Conn, m1)
	if h == nil {
		return errors.New(fmt.Sprintf("plugin.New return nil: %s", name))
	}
	obj, ok := h.(myinterface.ResObj)
	if ok == false {
		return errors.New(fmt.Sprintf("return object Type error: %s", name))
	}
	return obj.Run()
}
func main() {
	site1 := new(Site)
	defer site1.Close()
	pathname, err := os.Executable()
	if err != nil {
		panic(err)
	}
	dir1 := filepath.Dir(pathname)
	site1.Init(filepath.Join(dir1, "config.toml"))
	if err != nil {
		panic(err)
	}
	//Load all plugins
	cfgDir, err := os.Open(filepath.Join(dir1, "config.d"))
	if err != nil {
		panic(err)
	}
	names, err := cfgDir.Readdirnames(0)
	if err != nil {
		panic(err)
	}
	for _, n1 := range names {
		if strings.HasPrefix(n1, "0-") == false {
			continue
		}
		log.Printf("Read plugin: %s\n", n1)
		m1 := readPlugToml(filepath.Join(dir1, "config.d", n1))
		if m1 == nil {
			continue
		}
		p1 := m1["So"]
		path1 := p1.(string)
		err := site1.LoadPlug(path1, m1)
		log.Printf("Loaded plugin: %s, error:%v\n", filepath.Base(path1), err)
	}
	cfgDir.Close()
	//ioutil.WriteFile(filepath.Join(dir1, "static", "iris-ws.js"), websocket.ClientSource, 0644)
	site1.Run()
}
